package net.ufrog.common.utils;

import net.ufrog.common.CoreConfig;
import net.ufrog.common.Logger;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日历工具
 * 
 * @author ultrafrog
 * @version 1.0, 2011-12-19
 * @since 1.0
 */
public class Calendars {

    private static final Pattern PARSE_DAYS 		= Pattern.compile("^([0-9]+)d$");
    private static final Pattern PARSE_HOURS 		= Pattern.compile("^([0-9]+)h$");
    private static final Pattern PARSE_MINUTES 		= Pattern.compile("^([0-9]+)mi?n$");
    private static final Pattern PARSE_SECONDS 		= Pattern.compile("^([0-9]+)s$");

    private static ThreadLocal<Map<String, DateFormat>> current = new ThreadLocal<>();

    /** 构造函数 */
    public Calendars() {}

    /**
     * 读取格式化工具
     *
     * @param pattern 格式表达式
     * @return 格式化工具
     */
    public static DateFormat getFormat(String pattern) {
        Map<String, DateFormat> mDateFormat = current.get();
        if (mDateFormat == null || !mDateFormat.containsKey(pattern)) {
            if (mDateFormat == null) {
                mDateFormat = new HashMap<>();
                current.set(mDateFormat);
            }
            mDateFormat.put(pattern, new SimpleDateFormat(pattern));
        }
        return mDateFormat.get(pattern);
    }

    /**
     * 格式化时间
     *
     * @param pattern 格式表达式
     * @param date 待格式时间
     * @return 格式化后时间字符串
     */
    public static String format(String pattern, Date date) {
        return getFormat(pattern).format(date);
    }

    /**
     * 格式化当前时间
     *
     * @param pattern 格式表达式
     * @return 格式化后时间字符串
     */
    public static String format(String pattern) {
        return format(pattern, new Date());
    }

    /**
     * 解析时间字符串
     *
     * @param pattern 格式表达式
     * @param source 待解析时间字符串
     * @return 解析后时间对象
     * @throws ParseException 时间字符串与表达式不符
     */
    public static Date parse(String pattern, String source) throws ParseException {
        return getFormat(pattern).parse(source);
    }

    /**
     * 解析并格式化时间字符串
     *
     * @param parsePattern 解析表达式
     * @param formatPattern 格式表达式
     * @param source 待处理时间字符串
     * @return 格式化后时间字符串
     * @throws ParseException 时间字符串与表达式不符
     */
    public static String parseAndFormat(String parsePattern, String formatPattern, String source) throws ParseException {
        return format(formatPattern, parse(parsePattern, source));
    }

    /**
     * 解析并格式化时间字符串<br>
     * 如果发生异常返回默认字符串
     *
     * @param parsePattern 解析表达式
     * @param formatPattern 格式表达式
     * @param source 待处理时间字符串
     * @param def 默认字符串
     * @return 格式化后时间字符串或默认字符串
     */
    public static String parseAndFormat(String parsePattern, String formatPattern, String source, String def) {
        try {
            return parseAndFormat(parsePattern, formatPattern, source);
        } catch (ParseException e) {
            Logger.warn(e.getMessage());
            return def;
        }
    }

    /**
     * 格式化日期
     *
     * @param date 待格式时间
     * @return 格式化后时间字符串
     */
    public static String date(Date date) {
        return format(CoreConfig.getPatternDate(), date);
    }

    /**
     * 格式化当前日期
     *
     * @return 格式化后时间字符串
     */
    public static String date() {
        return date(new Date());
    }

    /**
     * 格式化日期位移
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @return 格式化后时间字符串
     */
    public static String date(Integer field, Integer amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(field, amount);
        return date(calendar.getTime());
    }

    /**
     * 格式化时间
     *
     * @param date 待格式时间
     * @return 格式化后时间字符串
     */
    public static String time(Date date) {
        return format(CoreConfig.getPatternTime(), date);
    }

    /**
     * 格式化当前时间
     *
     * @return 格式化后时间字符串
     */
    public static String time() {
        return time(new Date());
    }

    /**
     * 格式化时间位移
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @return 格式化后时间字符串
     */
    public static String time(Integer field, Integer amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(field, amount);
        return time(calendar.getTime());
    }

    /**
     * 格式化日期时间
     *
     * @param date 待格式时间
     * @return 格式化后时间字符串
     */
    public static String datetime(Date date) {
        return format(CoreConfig.getPatternDatetime(), date);
    }

    /**
     * 格式化当前日期时间
     *
     * @return 格式化后时间字符串
     */
    public static String datetime() {
        return datetime(new Date());
    }

    /**
     * 格式化时间日期位移
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @return 格式化后时间字符串
     */
    public static String datetime(Integer field, Integer amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(field, amount);
        return datetime(calendar.getTime());
    }

    /**
     * 解析日期
     *
     * @param source 待处理时间字符串
     * @return 解析后时间对象
     * @throws ParseException 时间字符串与表达式不符
     */
    public static Date parseDate(String source) throws ParseException {
        return parse(CoreConfig.getPatternDate(), source);
    }

    /**
     * 解析时间
     *
     * @param source 待处理时间字符串
     * @return 解析后时间对象
     * @throws ParseException 时间字符串与表达式不符
     */
    public static Date parseTime(String source) throws ParseException {
        return parse(CoreConfig.getPatternTime(), source);
    }

    /**
     * 解析日期时间
     *
     * @param source 待处理时间字符串
     * @return 解析后时间对象
     * @throws ParseException 时间字符串与表达式不符
     */
    public static Date parseDatetime(String source) throws ParseException {
        return parse(CoreConfig.getPatternDatetime(), source);
    }

    /**
     * 解析持续时间<br>
     * 返回秒数<br>
     * 样例：3d, 4h, 5mn, 6s
     *
     * @param duration 时间表达式
     * @return 解析后的时间秒数
     */
    public static int parseDuration(String duration) {
        // 初始化
        if (Strings.empty(duration)) duration = "30d";
        Matcher matcher = null;

        // 判断周期类型并转换成秒数
        if ((matcher = PARSE_DAYS.matcher(duration)).matches()) {
            return Integer.parseInt(matcher.group(1)) * 60 * 60 * 24;
        } else if ((matcher = PARSE_HOURS.matcher(duration)).matches()) {
            return Integer.parseInt(matcher.group(1)) * 60 * 60;
        } else if ((matcher = PARSE_MINUTES.matcher(duration)).matches()) {
            return Integer.parseInt(matcher.group(1)) * 60;
        } else if ((matcher = PARSE_SECONDS.matcher(duration)).matches()) {
            return Integer.parseInt(matcher.group(1));
        } else {
            throw new IllegalArgumentException(String.format("invalid duration pattern '%s'", duration));
        }
    }

    /**
     * 日期相减
     *
     * @param beginDate 开始时间
     * @param endDate 结束时间
     * @param dateType 时间类型
     * @return 按时间类型的差值
     * @throws ParseException 时间字符串与表达式不符
     */
    public static Long diff(Date beginDate, Date endDate, DateType dateType) throws ParseException {
        beginDate = parse(dateType.getPattern(), format(dateType.getPattern(), beginDate));
        endDate = parse(dateType.getPattern(), format(dateType.getPattern(), endDate));
        return (endDate.getTime() - beginDate.getTime()) / dateType.getMilliseconds();
    }

    /**
     * 截断日期<br>
     * 设置日期的时分秒为零
     *
     * @param calendar 日历对象
     * @return 结果日期对象
     */
    public static Date truncate(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 截断日期<br>
     * 设置日期的时分秒为零
     *
     * @param date 时间对象
     * @return 结果时间对象
     */
    public static Date truncate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return truncate(calendar);
    }

    /**
     * 日初<br>
     * 将日期格式成<code>yyyy-MM-dd 00:00:00.000</code>
     *
     * @param date 时间对象
     * @return 结果时间对象
     */
    public static Date begin(Date date) {
        return truncate(date);
    }

    /**
     * 日终<br>
     * 将日期格式成<code>yyyy-MM-dd 23:59:59.999</code>
     *
     * @param calendar 日历对象
     * @return 结果时间对象
     */
    public static Date end(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DATE, 1);
        calendar.add(Calendar.MILLISECOND, -1);
        return calendar.getTime();
    }

    /**
     * 日终<br>
     * 将日期格式成<code>yyyy-MM-dd 23:59:59.999</code>
     *
     * @param date 时间对象
     * @return 结果时间对象
     */
    public static Date end(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return end(calendar);
    }

    /**
     * 添加
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @param calendar 日历对象
     * @return 结果日历对象
     */
    public static Calendar add(Integer field, Integer amount, Calendar calendar) {
        calendar.add(field, amount);
        return calendar;
    }

    /**
     * 添加
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @param date 时间对象
     * @return 结果时间对象
     */
    public static Date add(Integer field, Integer amount, Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return add(field, amount, calendar).getTime();
    }

    /**
     * 添加<br>以当前日期为基准
     *
     * @param field 位移单位 参考 <code>Calendar</code> 对象
     * @param amount 位移量
     * @return 结果时间对象
     */
    public static Date add(Integer field, Integer amount) {
        return add(field, amount, new Date());
    }

    /**
     * 日期类型
     *
     * @author ultrafrog
     * @version 1.0, 2014-10-16
     * @since 1.0
     */
    public enum DateType {
        DATE(24 * 60 * 60 * 1000L, "yyyy-MM-dd"),
        HOUR(60 * 60 * 1000L, "yyyy-MM-dd HH"),
        MINUTE(60 * 1000L, "yyyy-MM-dd HH:mm"),
        SECOND(1000L, "yyyy-MM-dd HH:mm:ss");

        /** 毫秒数 */
        private Long milliseconds;

        /** 表达式 */
        private String pattern;

        /**
         * 构造函数
         *
         * @param milliseconds 毫秒数
         * @param pattern 表达式
         */
        DateType(Long milliseconds, String pattern) {
            this.milliseconds = milliseconds;
            this.pattern = pattern;
        }

        /**
         * 读取毫秒数
         *
         * @return 毫秒数
         */
        public Long getMilliseconds() {
            return milliseconds;
        }

        /**
         * 读取表达式
         *
         * @return 表达式
         */
        public String getPattern() {
            return pattern;
        }
    }
}
